package org.aurora.client.spring;

import org.apache.commons.lang3.StringUtils;
import org.aurora.client.ITaskProcessor;
import org.aurora.client.exception.AuroraTaskRegisterException;
import org.aurora.model.entity.ScheduleTaskContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AuroraTaskWrapper implements FactoryBean<ITaskProcessor>, BeanFactoryAware {

    private static final Logger log = LoggerFactory.getLogger(AuroraTaskWrapper.class);

    private String ref;

    private String method;

    private Object refInstance;

    private Method methodInstance;

    private BeanFactory beanFactory;

    private int taskContextIndex = -1;

    private Object[] methodArgs;

    @Override
    public ITaskProcessor getObject() {
        if (StringUtils.isBlank(this.ref)) {
            log.error("aurora task register error, ref is blank, ref = {}, method = {}", this.ref, this.method);
            throw new AuroraTaskRegisterException(String.format("aurora task register error, ref is blank, ref = %s, method = %s", this.ref, this.method));
        }

        if (StringUtils.isBlank(this.method)) {
            log.error("aurora task register error, method is blank, ref = {}, method = {}", this.ref, this.method);
            throw new AuroraTaskRegisterException(String.format("aurora task register error, method is blank, ref = %s, method = %s", this.ref, this.method));
        }

        this.refInstance = getRefInstance();
        this.methodInstance = getMethodInstance();

        return new ITaskProcessor() {
            @Override
            public void processor(ScheduleTaskContext taskContext) {
                try {
                    // 填充上下文参数
                    if (AuroraTaskWrapper.this.taskContextIndex != -1) {
                        AuroraTaskWrapper.this.methodArgs[AuroraTaskWrapper.this.taskContextIndex] = taskContext;
                    }
                    // 进行调用
                    AuroraTaskWrapper.this.methodInstance.invoke(AuroraTaskWrapper.this.refInstance);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                } finally {
                    // 上下文参数设置初始值
                    AuroraTaskWrapper.this.methodArgs[AuroraTaskWrapper.this.taskContextIndex] = null;
                }
            }
        };
    }

    private Object getRefInstance() {
        try {
            return this.beanFactory.getBean(this.ref);
        } catch (BeansException e) {
            log.error("aurora task register error, ref instance not exist, ref = {}, method = {}", this.ref, this.method);
            throw new AuroraTaskRegisterException(String.format("aurora task register error, method is blank, ref = %s, method = %s", this.ref, this.method), e);
        }
    }

    private Method getMethodInstance() {
        Class<?> clazz = this.refInstance.getClass();

        Method[] declaredMethods = clazz.getDeclaredMethods();

        Method result = null;

        int methodParamsSize = 0;

        for (Method method : declaredMethods) {
            String methodName = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();

            if (!StringUtils.equals(methodName, this.method)) {
                continue;
            }

            if (result != null) {
                log.error("aurora task register error, method instance repeat, ref = {}, method = {}", this.ref, this.method);
                throw new AuroraTaskRegisterException(String.format("aurora task register error, method instance repeat, ref = %s, method = %s", this.ref, this.method));
            }

            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];

                if (parameterType == ScheduleTaskContext.class) {
                    this.taskContextIndex = i;
                    break;
                }
            }
            methodParamsSize = parameterTypes.length;
            result = method;
        }

        if (result == null) {
            log.error("aurora task register error, method not exist, ref = {}, method = {}", this.ref, this.method);
            throw new AuroraTaskRegisterException(String.format("aurora task register error, method not exist, ref = %s, method = %s", this.ref, this.method));
        }

        this.methodArgs = new Object[methodParamsSize];
        return result;
    }

    @Override
    public Class<?> getObjectType() {
        return ITaskProcessor.class;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
